﻿namespace CoProjectDF.Views.Login
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using CoProjectDF;
    using CoProjectDF.Models.Login;
    using IdeaBlade.EntityModel;

    /// <summary>
    /// Form that presents the <see cref="RegistrationData"/> and performs the registration process.
    /// </summary>
    public partial class RegistrationForm : StackPanel
    {
        private LoginRegistrationWindow parentWindow;
        private RegistrationViewModel registrationViewModel;

        /// <summary>
        /// Creates a new <see cref="RegistrationForm"/> instance.
        /// </summary>
        public RegistrationForm()
        {
            InitializeComponent();
            this.DataContext = this.registrationViewModel = new RegistrationViewModel();
        }

        /// <summary>
        /// Sets the parent window for the current <see cref="RegistrationForm"/>.
        /// </summary>
        /// <param name="window">The window to use as the parent.</param>
        public void SetParentWindow(LoginRegistrationWindow window)
        {
            this.parentWindow = window;
        }

        /// <summary>
        /// Called by the parent window when close/cancel is wanted.
        /// </summary>
        /// <returns></returns>
        public bool CanClose()
        {
            if (!registrationViewModel.CanCancel) return false;
            registrationViewModel.Cancel();
            registerForm.CancelEdit();
            return true;
        }

        /// <summary>
        /// Wire up the Password and PasswordConfirmation Accessors as the fields are generated.
        /// </summary>
        private void RegisterForm_AutoGeneratingField(object dataForm, DataFormAutoGeneratingFieldEventArgs e)
        {
            // Put all the fields in adding mode
            e.Field.Mode = DataFieldMode.AddNew;

            if (e.PropertyName == "Password")
            {
                PasswordBox passwordBox = (PasswordBox)e.Field.Content;
                this.registrationViewModel.SetPasswordAccessor(() => passwordBox.Password);
            }
            else if (e.PropertyName == "PasswordConfirmation")
            {
                PasswordBox passwordConfirmationBox = (PasswordBox)e.Field.Content;
                this.registrationViewModel.SetPasswordConfirmationAccessor(() => passwordConfirmationBox.Password);
            }
            else if (e.PropertyName == "UserName")
            {
                TextBox textBox = (TextBox)e.Field.Content;
            }
            else if (e.PropertyName == "Question")
            {
                // Create a ComboBox populated with security questions
                ComboBox comboBoxWithSecurityQuestions = CreateComboBoxWithSecurityQuestions();

                // Replace the control
                // Note: Since TextBox.Text treats empty text as string.Empty and ComboBox.SelectedItem
                // treats an empty selection as null, we need to use a converter on the binding
                e.Field.ReplaceTextBox(comboBoxWithSecurityQuestions, ComboBox.SelectedItemProperty,
                                                binding => binding.Converter = new TargetNullValueConverter());
            }
        }

        /// <summary>
        /// Returns a <see cref="ComboBox" /> object whose <b>ItemsSource</b> property
        /// is initialized with the resource strings defined in <see cref="SecurityQuestions" />.
        /// </summary>
        private ComboBox CreateComboBoxWithSecurityQuestions()
        {
            ComboBox comboBox = new ComboBox();
            comboBox.ItemsSource = registrationViewModel.SecurityQuestions;
            return comboBox;
        }

        /// <summary>
        /// Submit the new registration.
        /// </summary>
        private void RegisterButton_Click(object sender, RoutedEventArgs e)
        {
            // We need to force validation since we are not using the standard OK
            // button from the DataForm.  Without ensuring the form is valid, we
            // would get an exception invoking the operation if the entity is invalid.
            if (this.registerForm.ValidateItem())
            {
                registrationViewModel.RegisterUser(RegistrationOperation_Completed);
            }
        }

        /// <summary>
        /// Completion handler for the registration operation. If there was an error 
        /// display a message, otherwise close the window.
        /// </summary>
        private void RegistrationOperation_Completed(BaseOperation operation)
        {
            if (!operation.Cancelled)
            {
                if (operation.HasError)
                {
                    ShowRegistrationError(operation.Error);
                    operation.MarkErrorAsHandled();
                }
                else
                {
                    this.parentWindow.Close();
                }
            }
        }

        /// <summary>
        /// Process the registration error.  Here we show a message box for certain failures.
        /// </summary>
        /// <param name="ex"></param>
        private void ShowRegistrationError(Exception ex)
        {
            var re = ex as RegistrationException;
            if (re == null)
            {
                ErrorWindow.CreateNew(ex);
                return;
            }
            if (re.Status == CreateUserStatus.DuplicateUserName)
            {
                MessageBox.Show(ErrorResources.CreateUserStatusDuplicateUserName);
            }
            else if (re.Status == CreateUserStatus.DuplicateEmail)
            {
                MessageBox.Show(ErrorResources.CreateUserStatusDuplicateEmail);
            }
            else
            {
                MessageBox.Show(ErrorResources.CreateUserStatusFailed);
            }
            // TODO: Should also handle validation failures.
        }

        /// <summary>
        /// Switches to the login window.
        /// </summary>
        private void BackToLogin_Click(object sender, RoutedEventArgs e)
        {
            this.parentWindow.NavigateToLogin();
        }

        /// <summary>
        /// Cancel if possible and close window.
        /// </summary>
        private void CancelButton_Click(object sender, EventArgs e)
        {
            this.parentWindow.Close();
        }

    }
}
